/***************************************************************************
* COSC1283/1284 - Programming Techniques
* Semester 1 2011 Assignment #1
* Full Name        : Le Yan 
* Student Number   : 3262302 
* Yallara Username : s3262302 
* Course Code      : 1284 
* Program Code     : BP232 
* Start up code provided by Christopher Hoobin and Xiaodong Li
***************************************************************************/

#include "assign1.h"
/* This source file contains important functions to be developed and
 * used by various menu options, as indicated. Note that every
 * function has as its first parameter the optionsStats array, which
 * will be appropriately updated for later reporting of menu option 6.
 * You may ignore this parameter and its relevance to each function
 * until you develop the sessionSummary() function.
 */
#define PADDINGNUM 8

/* Function reverse reverse the st*/
void reverse(char *st)
{
  int n = strlen(st);
  char tmp[20];
  int i, j;

  for(i=n-1, j=0; i>=0; i--, j++)
     tmp[j] = st[i]; 

  tmp[j] = '\0';
  strcpy(st, tmp);
}


/* Function baseconvert converts a decimal digit to any base*/
void baseconvert(int digit, int base, char *st)
{ 
  int i=0;
  while( digit > 0 )
  {
    int tmp = digit % base;
    
    if( tmp < 10 )
     st[i++] = '0' + tmp;
    else
     st[i++] = tmp - 10 + 'A';  

    digit /= base; 
  }
  st[i] = '\0';
  reverse(st);
}


/* Function asciiToBinary() converts a given character (inputChar) to
 * its 8-bit binary equivalent, stored as a string of 0s and 1s in
 * binaryString. This function will be used in menu option 1.
 */
void asciiToBinary(char inputChar, char *binaryString)
{

  baseconvert(inputChar, 2, binaryString);
  
}

/* Function asciiToHexadecimal() converts a given character (inputChar) to
 * its hexadecimal (base 16) equivalent, stored as a string of
 * hexadecimal digits in hexString. This function will be used in menu
 * option 1.
 */
void asciiToHexadecimal(char inputChar, char *hexString)
{

  baseconvert(inputChar, 16, hexString);

}

/* paddingZero print  n-strlen(st) zero */
void paddingZero(char *st, int n)
{
   int i;
   for(i=0; i<n-strlen(st); i++)
      printf("0");
 
}


/* Function asciiTranslation() converts each character in a string
 * (asciiString) into its ascii, binary and hexadecimal form. For the
 * given string you are required to generate a table with three
 * columns. Each character of the string should be displayed in the
 * first column, with the other two columns containing the
 * corresponding binary and hexadecimal representations of the
 * character. You must use the two function prototypes provided above
 * for translation. This function will be used in menu option 1.
 */
void asciiTranslation(int *optionStats, char *asciiString)
{
    int i;
    char tmp[LEN];
    //convert begins here

    if( optionStats != NULL )
     optionStats[ASCII]++;  

    printf("Character   Binary   Hexadeciaml\n");
   
    for(i=0; i<strlen(asciiString); i++)
    {
      printf("     %c      ", asciiString[i]);
      asciiToBinary(asciiString[i], tmp); 
      //padding zero, if the length of binary number is less than 8
      paddingZero(tmp, PADDINGNUM); 
      printf("%s   ", tmp);
      asciiToHexadecimal(asciiString[i], tmp);
      printf("%s   \n", tmp); 
    }
    printf("\n");
}


/* Function displayClock() will display a clock as per menu option 2,
 * using the hours and minutes arguments. These are assumed to reflect
 * 24-hour time, or else the function returns an error code. This
 * function has been partially written for you. This function will be
 * used in menu option 2. (A test driver for displayClock() is
 * provided in the file dc.c, which may be compiled separately.
 */
int displayClock(int *optionStats, int h, int m)
{
    /* The array to hold the hard-coded clock. Note: +1 for \0 */
    char clock[CLOCK_HEIGHT][CLOCK_WIDTH + 1] = {
        "        12        ",
        "   11        01   ",
        " 10            02 ",
        "                  ",
        "09      .       03",
        "                  ",
        " 08            04 ",
        "   07        05   ",
        "        06        "
      };

    /* Row and column numbers in clock array for hour positions. For
     * example, the "06" in the clock display is stored at position
     * [8, 8] in the clock matrix (see above) */
    const int hr[CLOCK_TICKS] = {0,  1,  2,  4,  6,  7, 8, 7, 6, 4, 2, 1, 0};
    const int hc[CLOCK_TICKS] = {0, 13, 15, 16, 15, 13, 8, 3, 1, 0, 1, 3, 8};

    /* Hour and minute codes */
    const char hs = 'H', ms = 'm';

    int i, j; /* Declare other variables as needed */

    if( optionStats != NULL )
     optionStats[CLOCK1]++; 
    /* The next 5 comments require code to be developed by you! */

    /* Ensure that h and m are in range and, if not, return
     * appropriate error value. Otherwise adjust h if it is 0 or it
     * exceeds 12
     */
    if ( h>=0 && h<24 && m>=0 && m<60 )
    {
      if ( h> 12 )
        h -= 12; 
      if ( h == 0 )
        h = 12;
    }
    else
      return -1;

    /* Now update the clock array accordingly, with the hour symbol */
    clock[ hr[h] ][ hc[h] ] = clock[ hr[h] ][ hc[h]+1 ] = hs; 

    /* Round m to the nearest hour position */
    if( m%5 != 0 )
    {
       m = (m%5 > 2)?m/5 + 1:m/5; 
    } 
    else
    {
      m /= 5; 
    }

    /* Update clock with minute symbol, taking into account a "clash"
     * with the hour symbol
     */
    if ( m == 0 )
      m = 12;
    
    if( m == h )
    {
      clock[ hr[m] ][ hc[m] ] = ms; 
    }
    else
    {
      clock[ hr[m] ][ hc[m] ] = clock[ hr[m] ][ hc[m]+1 ] = ms;  
    } 
    /* Display the clock */

    for (i = 0; i < CLOCK_HEIGHT; i++)
    {
        for (j = 0; j < CLOCK_WIDTH; j++)
    	    printf("%c", clock[i][j]);
        printf("\n");
    }
    printf("\n");

    return 0;
}


/* Function displayClock2() will display a clock as per menu option 2,
 * but after first obtaining and displaying the current date/time in
 * text form (as per date command). Apart from adding code to display
 * the text date/time, its remaining functionality will be identical
 * to displayClock(). This function will be used in menu option 3.
 */
void displayClock2(int *optionStats)
{
  time_t timer;
  struct tm *t;

  if( optionStats != NULL )
   optionStats[CLOCK2]++;

  printf("Current date/time display\n");
  printf("-------------------------\n");
  printf("\n");
  
  timer = time(NULL);
  t = localtime(&timer);
  printf("%s\n", asctime(t));  

  displayClock(NULL , t->tm_hour, t->tm_min); 
}


//convert single roman numberial to demical
int romanToDec(char c)
{
   char *roman = "MDCLXVI";
   int decimal[7] = { 1000, 500, 100, 50, 10, 5, 1 };
   int i;


   for(i=0; i<strlen(roman); i++)
   {
     if( c == roman[i] )
     {
   //   printf("%d ", decimal[i]);
      return decimal[i]; 

     }
   }  
   return -1;
}

/* This function converts a Roman numeral into its decimal equivalent.
 * The function prompts the user for a Roman numeral, which is entered
 * as a character string. The function first converts the Roman numeral
 * to decimal and then prints the decimal numeral. The function halts
 * when the user enters a string that is not a valid Roman numeral.
 * This function will be used in menu option 4.
 */
void convertRomanNumeral(int *optionStats, char *romanNumeral)
{
   int i , convert, n;
   n = strlen(romanNumeral);
  
   if( optionStats != NULL )
     optionStats[ROMAN]++;

   for(i=0, convert=0; ; i++)
   {
          
     if ( i+1 == n) 
     {
       convert += romanToDec(romanNumeral[i]);
       break; 
     }

     if ( romanToDec(romanNumeral[i]) >= romanToDec(romanNumeral[i+1]) ) 
       convert += romanToDec(romanNumeral[i]);
     else
       convert -= romanToDec(romanNumeral[i]);

   }  
   
   printf("The decimal equivalent is: %d\n\n", convert); 
     
}

// randomInt will return a number from 0 ~ range
int randomInt(int range)
{
  //seeds the random number generator
  srand((unsigned int)time(NULL));
  return rand()%range;
}

    
/* Function minesweeper() creates a field of N x M for the game of
* minesweeper. The function first reads in integers N and M, and the
* number of mines allowed, then output a field of the same size,
* with the "." characters replaced by the number of mines adjacent to
* that square. Note that the mine squares are randomly placed on the field.
* (see requirement#6 for an example). This function will be used in
 * menu option 5.
 */
void minesweeper(int *optionStats, int m, int n, int numMines)
{
   int i, j, tmp, p;
   char field1[m][n];//the field on the left
   char field2[m][n];//the field on the right
   int position[m*n]; //set which stores all possible positions of field
   char *point;


   if( optionStats != NULL )
     optionStats[MINESWEEPER]++;
 
   //init
   for(i=0; i<m; i++)
    for(j=0; j<n; j++)
    {
      field1[i][j] = '.';
    }
   for(i=0; i<n*m; i++)
   {
     position[i] = i;
   } 
   p = n*m-1;// p point to the end of set

 
   //set mine
   point = &field1[0][0];
   for(i=0; i<numMines; i++)
   {
       tmp = randomInt(p+1);//get position from set
       *(point+position[tmp]) = '*';  //set mine
       position[tmp] = position[p];  //discard used position
       p--;//size of set decrease by 1
   }

   printf("\n"); 
   //generate hint numbers   
   for(i=0; i<m; i++)
   {
      for(j=0; j<n; j++)
      {
        if( field1[i][j] == '*' )
        {
          field2[i][j] = '*';
          continue;      
        }
 
        tmp = 0; 
        if( i-1 >= 0)
        {
          if( field1[i-1][j] == '*' )
            tmp++;

          if( j-1 >= 0)
            if( field1[i-1][j-1] == '*' )
              tmp++;

          if( j+1 <n )
            if( field1[i-1][j+1] == '*')
              tmp++;   
        }       

        if( i+1 < m)
        {
          if( field1[i+1][j] == '*' )
           tmp++;
         
          if( j-1 >= 0 )
            if( field1[i+1][j-1] == '*')
              tmp++;

          if( j+1 < n)
            if( field1[i+1][j+1] == '*')
              tmp++; 
        }

        if( j-1 >= 0 )
          if( field1[i][j-1] == '*' )
            tmp++;
        
        if( j+1 < n )
          if( field1[i][j+1] == '*' )
            tmp++;

        field2[i][j] = '0' + tmp;
      }
   }
    
   //print field;    
   for(i=0; i<m; i++)
   {
       for(j=0; j<n; j++)
       {
         printf("%c", field1[i][j]); 
       }

       printf("     ");
      
       for(j=0; j<n; j++)
       {
         printf("%c", field2[i][j]); 
       }
       printf("\n");
   }   
    printf("\n"); 
  
  
}


/* See requirement #7  "Session summary" of the assignment specs.
 * Function sessionSummary() reports the program usage.
 */
void sessionSummary(int *optionStats)
{
  int i;
  
  if( optionStats != NULL )
   optionStats[SESSIONSUM]++;

  printf("Session summary\n");
  printf("---------------\n\n");
  printf("Option  Count\n");
  printf("------  -----\n");

  for(i=0; i<NUM_OPTION_STATS; i++)
  {
     printf("%6d %6d\n", i+1, optionStats[i]);    
  } 
  printf("\n");
}
